package com.ficus.road.maintain.core.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;

/**
 * Created by siyuan.li on 2020/4/17.
 */
@Slf4j
public class JsonUtils {

    public static final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());

    public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {

        Iterator<String> fieldNames = updateNode.fieldNames();
        while (fieldNames.hasNext()) {

            String fieldName = fieldNames.next();
            JsonNode jsonNode = mainNode.get(fieldName);
            // if field exists and is an embedded object
            if (jsonNode != null && jsonNode.isObject()) {
                merge(jsonNode, updateNode.get(fieldName));
            } else {
                if (mainNode instanceof ObjectNode) {
                    // Overwrite field
                    JsonNode value = updateNode.get(fieldName);
                    ((ObjectNode) mainNode).put(fieldName, value);
                }
            }

        }
        return mainNode;
    }

    public static JsonNode convert2JsonNode(Object object) {
        return objectMapper.convertValue(object, JsonNode.class);
    }

    public static <T> T treeToValue(JsonNode jsonNode, Class<T> clazz) {
        try {
            return objectMapper.treeToValue(jsonNode, clazz);
        } catch (JsonProcessingException e) {
            log.error("jsonNode parse error, jsonNode: {}", jsonNode, e);
            throw new RuntimeException(e);
        }
    }

    public static <T> T merge(Object mainObject, Object updateObject, Class<T> clazz) {
        JsonNode mainNode = convert2JsonNode(mainObject);
        JsonNode updateNode = convert2JsonNode(updateObject);
        JsonNode merge = merge(mainNode, updateNode);
        return treeToValue(merge, clazz);
    }

    public static String ObjectConvertorString(Object object) {
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.error("JsonUtils ObjectConvertorString faild!!!");
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T StringConvertorObject(String str, Class<T> clzz) {
        if (StringUtils.isEmpty(str)) {
            return null;
        }
        try {
            return objectMapper.readValue(str, clzz);
        } catch (IOException e) {
            log.error("JsonUtils StringConvertorObject faild!!!");
            e.printStackTrace();
        }
        return null;
    }

    public static <T> List<T> StringConvertToList(String str,  TypeReference ref) {
        if (StringUtils.isEmpty(str)) {
            return null;
        }
        try {
            return objectMapper.readValue(str, ref);
        } catch (IOException e) {
            log.error("JsonUtils StringConvertToList faild!!!");
            e.printStackTrace();
        }
        return null;
    }


    public static <T> T deepCopy(T source, Class<T> clazz) {
        T target = null;
        try {
            target = objectMapper.readValue(objectMapper.writeValueAsString(source), clazz);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return target;
    }

    /**
     * json 转 List<T>
     */
    public static <T> List<T> jsonToList(String jsonString, Class<T> clazz) {
        ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
        List<T> objList = null;
        try {
            JavaType t = mapper.getTypeFactory().constructParametricType(
                    List.class, clazz);
            objList = mapper.readValue(jsonString, t);
        } catch (Exception e) {
            log.error("jsonToList throw exception", e);
        }
        return objList;
    }
}
